<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>享元模式</title>
  </head>
  <body>
    <i>
      享元模式要求将对象的属性划分为内部状态与外部
      状态（状态在这里通常指属性）。享元模式的目标是尽量减少共享对象的数量，关于如何划分内
      部状态和外部状态，下面的几条经验提供了一些指引。
    </i>
    <ul>
      <li>内部状态存储于对象内部</li>
      <li>内部状态可以被一些对象共享</li>
      <li>内部状态独立于具体的场景，通常不会改变。</li>
      <li>外部状态取决于具体的场景，并根据场景而变化，外部状态不能被共享</li>

      享元模式带来的好处很大程度上取决于如何使用以及何时使用，一般来说，以下情况发生时 便可以使用享元模式。
      <p> 一个程序中使用了大量的相似对象。</p>
      <p> 由于使用了大量对象，造成很大的内存开销。</p>
      <p> 对象的大多数状态都可以变为外部状态。</p>
      <p> 剥离出对象的外部状态之后，可以用相对较少的共享对象取代大量对象。 可以看到，文件上传的例子完全符合这四点。</p>
    </ul>

    <script>
      var Model = function (sex) {
        this.sex = sex;
      };
      Model.prototype.takePhoto = function () {
        console.log('sex= ' + this.sex + ' underwear=' + this.underwear);
      };
      // 分别创建一个男模特对象和一个女模特对象：
      var maleModel = new Model('male'),
        femaleModel = new Model('female');
      // 给男模特依次穿上所有的男装，并进行拍照：
      for (var i = 1; i <= 50; i++) {
        maleModel.underwear = 'underwear' + i;
        maleModel.takePhoto();
      }
      // 同样，给女模特依次穿上所有的女装，并进行拍照：
      for (var j = 1; j <= 50; j++) {
        femaleModel.underwear = 'underwear' + j;
        femaleModel.takePhoto();
      }

      //没有外部状态的享元 => 对象池模式
      const objectPoolFactory = function (createObjFn) {
        const objectPool = [];
        return {
          // 从对象池创建
          create: function (...args) {
            const obj = objectPool.length === 0 ? createObjFn.apply(this, args) : objectPool.shift();
            return obj;
          },
          // 回收到对象池
          recover: function (obj) {
            objectPool.push(obj);
          },
        };
      };

      // 现在利用 objectPoolFactory 来创建一个装载一些 iframe 的对象池：
      const iframeFactory = objectPoolFactory(function () {
        const iframe = document.createElement('iframe');
        document.body.appendChild(iframe);
        iframe.onload = function () {
          iframe.onload = null; // 防止 iframe 重复加载的 bug
          iframeFactory.recover(iframe); // iframe 加载完成之后回收节点
        };
        return iframe;
      });

      const iframe1 = iframeFactory.create();
      iframe1.src = 'http:// baidu.com';
      const iframe2 = iframeFactory.create();
      iframe2.src = 'http:// QQ.com';
      setTimeout(function () {
        const iframe3 = iframeFactory.create();
        iframe3.src = 'http:// 163.com';
      }, 30);
    </script>
  </body>
</html>
